﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

using Framework.Graphics;
using Framework.Graphics.Light;


namespace Framework.Graphics.Light.Bump
{
    public class BumpManager
    {
        private float _specularStrength = 1.0f;

        private Color _ambientLight = new Color(.1f, .1f, .1f, 1);

        private GraphicsDevice _graphics;

        private RenderTarget2D _colorMapRenderTarget;
        private RenderTarget2D _normalMapRenderTarget;
        private RenderTarget2D _shadowMapRenderTarget;
        

        private List<Light> _lights;

        public List<Light> Lights
        {
            get { return _lights; }
            set { _lights = value; }
        }

        private Effect _lightEffect;
        private Effect _lightCombinedEffect;

        private EffectTechnique _lightEffectTechniquePointLight;
        private EffectParameter _lightEffectParameterStrength;
        private EffectParameter _lightEffectParameterPosition;
        private EffectParameter _lightEffectParameterConeDirection;
        private EffectParameter _lightEffectParameterLightColor;
        private EffectParameter _lightEffectParameterLightDecay;
        private EffectParameter _lightEffectParameterScreenWidth;
        private EffectParameter _lightEffectParameterScreenHeight;
        private EffectParameter _lightEffectParameterNormapMap;

        private EffectTechnique _lightCombinedEffectTechnique;
        private EffectParameter _lightCombinedEffectParamAmbient;
        private EffectParameter _lightCombinedEffectParamLightAmbient;
        private EffectParameter _lightCombinedEffectParamAmbientColor;
        private EffectParameter _lightCombinedEffectParamColorMap;
        private EffectParameter _lightCombinedEffectParamShadowMap;
        private EffectParameter _lightCombinedEffectParamNormalMap;

        private VertexPositionColorTexture[] _vertices;
        private VertexBuffer _vertexBuffer;

        public void Init(ContentManager CM, GraphicsDevice GD)
        {
            _graphics = GD;

            PresentationParameters pp = GD.PresentationParameters;
            int width = pp.BackBufferWidth;
            int height = pp.BackBufferHeight;
            SurfaceFormat format = pp.BackBufferFormat;

            _vertices = new VertexPositionColorTexture[4];
            _vertices[0] = new VertexPositionColorTexture(new Vector3(-1, 1, 0), Color.White, new Vector2(0, 0));
            _vertices[1] = new VertexPositionColorTexture(new Vector3(1, 1, 0), Color.White, new Vector2(1, 0));
            _vertices[2] = new VertexPositionColorTexture(new Vector3(-1, -1, 0), Color.White, new Vector2(0, 1));
            _vertices[3] = new VertexPositionColorTexture(new Vector3(1, -1, 0), Color.White, new Vector2(1, 1));

            _vertexBuffer = new VertexBuffer(GD, typeof(VertexPositionColorTexture), _vertices.Length, BufferUsage.None);
            _vertexBuffer.SetData(_vertices);

            _colorMapRenderTarget = new RenderTarget2D(GD, width, height);
            _normalMapRenderTarget = new RenderTarget2D(GD, width, height);
            _shadowMapRenderTarget = new RenderTarget2D(GD, width, height, false, format, pp.DepthStencilFormat, pp.MultiSampleCount, RenderTargetUsage.DiscardContents);

            _lightEffect = CM.Load<Effect>(@"Effect/MultiTarget");
            _lightCombinedEffect = CM.Load<Effect>(@"Effect/DeferredCombined");

            _lightEffectTechniquePointLight = _lightEffect.Techniques["DeferredPointLight"];
            _lightEffectParameterConeDirection = _lightEffect.Parameters["coneDirection"];
            _lightEffectParameterLightColor = _lightEffect.Parameters["lightColor"];
            _lightEffectParameterLightDecay = _lightEffect.Parameters["lightDecay"];
            _lightEffectParameterNormapMap = _lightEffect.Parameters["NormalMap"];
            _lightEffectParameterPosition = _lightEffect.Parameters["lightPosition"];
            _lightEffectParameterScreenHeight = _lightEffect.Parameters["screenHeight"];
            _lightEffectParameterScreenWidth = _lightEffect.Parameters["screenWidth"];
            _lightEffectParameterStrength = _lightEffect.Parameters["lightStrength"];

            _lightCombinedEffectTechnique = _lightCombinedEffect.Techniques["DeferredCombined2"];
            _lightCombinedEffectParamAmbient = _lightCombinedEffect.Parameters["ambient"];
            _lightCombinedEffectParamLightAmbient = _lightCombinedEffect.Parameters["lightAmbient"];
            _lightCombinedEffectParamAmbientColor = _lightCombinedEffect.Parameters["ambientColor"];
            _lightCombinedEffectParamColorMap = _lightCombinedEffect.Parameters["ColorMap"];
            _lightCombinedEffectParamShadowMap = _lightCombinedEffect.Parameters["ShadingMap"];
            _lightCombinedEffectParamNormalMap = _lightCombinedEffect.Parameters["NormalMap"];

            // Only test
            _lights = new List<Light>();
            //_lights.Add(new PointLight()
            //                    {
            //                        IsEnabled = true,
            //                        Color = new Vector4(1.0f, 1.0f, 1.0f, 1.0f),
            //                        Power = .7f,
            //                        LightDecay = 200,
            //                        Position = new Vector3(100, 100, 80)
            //                    });
            //_lights.Add(new PointLight()
            //{
            //    IsEnabled = true,
            //    Color = new Vector4(1.0f, 1.0f, 1.0f, 1.0f),
            //    Power = .3f,
            //    LightDecay = 500,
            //    Position = new Vector3(100, 100, 125)
            //});

        }

        public void Update()
        {
            //_lights[0].Position = new Vector3(Mouse.GetState().X, Mouse.GetState().Y, _lights[0].Position.Z);
            // Only test
        }

        public void Draw(RenderTarget2D colorMapRenderTarget,
            RenderTarget2D normalMapRenderTarget,
            LightManager LM)
        {
            _graphics.Clear(Color.Black);
            _normalMapRenderTarget = normalMapRenderTarget;
            _colorMapRenderTarget = colorMapRenderTarget;

            //_graphics.SetRenderTarget(_colorMapRenderTarget);
            _graphics.Clear(Color.Transparent);
            DrawColorMap(LM);

            _graphics.SetRenderTarget(null);
            //_graphics.SetRenderTarget(_normalMapRenderTarget);
            _graphics.Clear(Color.Transparent);
            DrawNormalMap(LM);

            _graphics.SetRenderTarget(null);
            GenerateShadowMap();
            _graphics.Clear(Color.Black);

            DrawCombinedMaps(null);

            //////returnTarget = DrawCombinedMaps(returnTarget);

            //_graphics.Clear(Color.Black);

            //Camera.SpriteBatch.Begin();
            //DrawCombinedMaps(null);
            //Camera.SpriteBatch.End();
        }

        private void DrawNormalMap(LightManager LM)
        {
            Camera.SpriteBatch.Begin();
            LM.DrawBumpNormal();
            Camera.SpriteBatch.End();
        }

        private void DrawColorMap(LightManager LM)
        {
            Camera.SpriteBatch.Begin();
            LM.DrawBumpColor();
            Camera.SpriteBatch.End();
        }

        private Texture2D GenerateShadowMap()
        {
            _graphics.SetRenderTarget(_shadowMapRenderTarget);
            _graphics.Clear(Color.Transparent);

            foreach (var light in _lights)
            {
                if (!light.IsEnabled) continue;

                _graphics.SetVertexBuffer(_vertexBuffer);

                // Draw all the light sources
                _lightEffectParameterStrength.SetValue(light.ActualPower);
                _lightEffectParameterPosition.SetValue(light.Position);
                _lightEffectParameterLightColor.SetValue(light.Color);
                _lightEffectParameterLightDecay.SetValue(light.LightDecay); // Value between 0.00 and 2.00   
                _lightEffect.Parameters["specularStrength"].SetValue(_specularStrength);

                if (light.LightType == LightType.Point)
                {
                    _lightEffect.CurrentTechnique = _lightEffectTechniquePointLight;
                }

                _lightEffectParameterScreenWidth.SetValue(_graphics.Viewport.Width);
                _lightEffectParameterScreenHeight.SetValue(_graphics.Viewport.Height);
                _lightEffect.Parameters["ambientColor"].SetValue(_ambientLight.ToVector4());
                _lightEffectParameterNormapMap.SetValue(_normalMapRenderTarget);
                _lightEffect.Parameters["ColorMap"].SetValue(_colorMapRenderTarget);
                _lightEffect.CurrentTechnique.Passes[0].Apply();

                // Add Belding (Black background)
                _graphics.BlendState = BlendBlack;

                // Draw some magic
                _graphics.DrawUserPrimitives(PrimitiveType.TriangleStrip, _vertices, 0, 2);
            }

            // Deactive the rander targets to resolve them
            _graphics.SetRenderTarget(null);

            return _shadowMapRenderTarget;
        }

        private RenderTarget2D DrawCombinedMaps(RenderTarget2D returnTarget)
        {
            _lightCombinedEffect.CurrentTechnique = _lightCombinedEffectTechnique;
            _lightCombinedEffectParamAmbient.SetValue(1f);
            _lightCombinedEffectParamLightAmbient.SetValue(4);
            _lightCombinedEffectParamAmbientColor.SetValue(_ambientLight.ToVector4());
            _lightCombinedEffectParamColorMap.SetValue(_colorMapRenderTarget);
            _lightCombinedEffectParamShadowMap.SetValue(_shadowMapRenderTarget);
            _lightCombinedEffectParamNormalMap.SetValue(_normalMapRenderTarget);
            _lightCombinedEffect.CurrentTechnique.Passes[0].Apply();

            _graphics.SetRenderTarget(null);
            _graphics.SetRenderTarget(returnTarget);

            Camera.SpriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, null, null, null, _lightCombinedEffect);
            Camera.SpriteBatch.Draw(_colorMapRenderTarget, Vector2.Zero, Color.White);
            Camera.SpriteBatch.End();

            _graphics.SetRenderTarget(null);

            return returnTarget;
        }

        public static BlendState BlendBlack = new BlendState()
        {
            ColorBlendFunction = BlendFunction.Add,
            ColorSourceBlend = Blend.One,
            ColorDestinationBlend = Blend.One,

            AlphaBlendFunction = BlendFunction.Add,
            AlphaSourceBlend = Blend.SourceAlpha,
            AlphaDestinationBlend = Blend.One
        };
    }
}
